package com.chat.business.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chat.business.common.thread_local.UserIdThreadLocal;
import com.chat.business.mapper.GroupMapper;
import com.chat.business.service.IGroupService;
import com.chat.business.service.IMessageChannelMappingService;
import com.chat.business.service.IUserService;
import com.chat.business.utils.RedisHashUtil;
import com.chat.common.common.constant.RedisKeyConstant;
import com.chat.common.dto.GetGroupDTO;
import com.chat.common.dto.GroupDTO;
import com.chat.common.entity.Group;
import com.chat.common.entity.MessageChannelMapping;
import com.chat.common.entity.User;
import com.chat.common.enums.NoticeType;
import com.chat.common.form.ListForm;
import com.chat.common.utils.DtoVoUtil;
import com.chat.common.vo.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author weikjie
 * @since 2024-03-29
 */
@Service
@Slf4j
public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements IGroupService {

    @Lazy
    @Resource
    private IMessageChannelMappingService messageChannelMappingService;
    @Resource
    private IUserService userService;
    @Resource
    private RedisHashUtil redisHashUtil;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private RedisTemplate<String, Object> redisTemplateString;

    @Resource
    private RedissonClient redissonClient;

    /**
     * 创建群
     *
     * @param groupDTO
     * @return
     */
    @Override
    @Transactional//事务
    public R<GroupVO> createGroup(GroupDTO groupDTO) {
        Group group = new Group();
        group.setGroupAvatar(groupDTO.getGroupAvatar());
        group.setGroupName(groupDTO.getGroupName());
        group.setGroupLeaderId(UserIdThreadLocal.get());
        group.setGroupAccount(System.currentTimeMillis() + RandomUtil.randomInt(1000, 9999) + "");//群号
        save(group);//保存群
        //查找出创建群聊时拉的人的信息
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(User::getAccount, groupDTO.getUserAccounts());
        //保存人和群的消息频道记录 批量
        List<User> userList = userService.list(lambdaQueryWrapper);
        //群主账号
        AtomicReference<String> groupLeaderAccount = new AtomicReference<>();
        List<MessageChannelMapping> collect = userList.stream()
                .map(user -> {
                    if (Objects.equals(user.getId(), UserIdThreadLocal.get())) {
                        groupLeaderAccount.set(user.getAccount());
                    }
                    MessageChannelMapping messageChannelMapping = new MessageChannelMapping();
                    messageChannelMapping.setFromId(user.getId());
                    messageChannelMapping.setToId(group.getId());
                    messageChannelMapping.setIsGroup(true);
                    messageChannelMapping.setIsActive(true);
                    return messageChannelMapping;
                }).collect(Collectors.toList());
        messageChannelMappingService.saveBatch(collect);
        List<UserVo> userVoList = userList.stream().map(user -> DtoVoUtil.entityToDtoOrVo(user, UserVo.class)).collect(Collectors.toList());
        GroupVO groupVO = DtoVoUtil.entityToDtoOrVo(group, GroupVO.class);
        groupVO.setUserVoList(userVoList);
        groupVO.setGroupLeaderAccount(groupLeaderAccount.get());
        groupVO.setGroupMsgGetLastTimeMilli(0L);
        //TODO：通知被拉入群的人
        userList.forEach(user -> {
            Map<String, Object> noticeData = NoticeVO.NoticeDataBuilder.builder().put("groupVO", groupVO).build();
            NoticeVO noticeVO = NoticeVO.builder().noticeType(NoticeType.groupCreate)
                    .publishId(UserIdThreadLocal.get())
                    .consumerId(user.getId())
                    .data(noticeData)
                    .date(new Date())
                    .build();
            //更新redis 群成员缓存
            RLock lock = redissonClient.getLock("Lock:" + RedisKeyConstant.USER_GROUP_CACHE + user.getId());
            lock.lock(5, TimeUnit.SECONDS);//加锁，阻塞式等待 5s，默认加的锁都是30s
            //先去Redis中查缓存
            String json = (String) redisTemplateString.opsForValue().get(RedisKeyConstant.USER_GROUP_CACHE + user.getId());
            List<GroupVO> groupVOS;
            if (json == null) {
                groupVOS = new ArrayList<>();
            } else {
                groupVOS = JSONUtil.toList(json, GroupVO.class);
            }
            groupVOS.add(groupVO);
            redisTemplate.opsForValue().set(RedisKeyConstant.USER_GROUP_CACHE + user.getId(), JSONUtil.toJsonStr(groupVOS));
            lock.unlock();//更新完成，释放锁
            redisHashUtil.publishNotice(noticeVO);
        });
        return R.succeed(groupVO);//返回群信息
    }

    /**
     * 解散群
     *
     * @param groupDTO
     * @return
     */
    @Override
    public R disbandGroup(GroupDTO groupDTO) {
        //先查询这个群是否存在
        Group group = getById(groupDTO.getId());
        if (group == null)
            return R.failed("id为" + groupDTO.getId() + "的群不存在");
        //判断是不是群主本人操作
        if (!group.getGroupLeaderId().equals(UserIdThreadLocal.get()))
            return R.failed("你不是群主，没有解散群的权限");
        //解散群
        removeById(group);
        //TODO: 给每个群成员发送解散通知

        //获取这个群的所有成员id
        List<Long> groupMemberIds = messageChannelMappingService.getGroupMemberIds(group.getId());
        //通知信息
        Map<String, Object> noticeData = new HashMap<>();
        GroupVO groupVO = DtoVoUtil.entityToDtoOrVo(group, GroupVO.class);
        noticeData.put("groupVO", groupVO);//被解散的群信息
        //给每一个成员发送通知
        groupMemberIds.forEach(id -> {
            //构建通知体
            NoticeVO noticeVO = NoticeVO.builder().noticeType(NoticeType.groupDisbanded)
                    .publishId(group.getId())
                    .consumerId(id)
                    .date(new Date())
                    .data(noticeData).build();
            //发送通知
            redisHashUtil.publishNotice(noticeVO);
        });
        return R.succeed("群已解散");
    }

    /**
     * 更新群信息
     *
     * @param groupDTO
     * @return
     */
    @Override
    public R updateGroupData(GroupDTO groupDTO) {
        //先查询这个群是否存在
        Group group = getById(groupDTO.getId());
        if (group == null)
            return R.failed("群不存在");
        //判断是不是群主本人操作
        if (!group.getGroupLeaderId().equals(UserIdThreadLocal.get()))
            return R.failed("你不是群主，没有修改群的权限");
        updateById(group);
        //TODO: 给每个群成员发送群信息修改通知
        Map<String, Object> noticeData = new HashMap<>();
        GroupVO groupVO = DtoVoUtil.entityToDtoOrVo(group, GroupVO.class);
        noticeData.put("groupVO", groupVO);//被修改的群信息
        //获取这个群的所有成员id
        List<Long> groupMemberIds = messageChannelMappingService.getGroupMemberIds(group.getId());
        //给每一个成员发送通知
        groupMemberIds.forEach(id -> {
            //构建通知体
            NoticeVO noticeVO = NoticeVO.builder().noticeType(NoticeType.groupInfoUpdate)
                    .publishId(group.getId())
                    .consumerId(id)
                    .date(new Date())
                    .data(noticeData).build();
            //发送通知
            redisHashUtil.publishNotice(noticeVO);
        });
        return R.succeed("修改成功", groupVO);
    }

    /**
     * 根据群号或者群名查询相关群
     *
     * @param keyword
     * @return
     */
    @Override
    public List<Group> findByKeyword(String keyword) {
        LambdaQueryWrapper<Group> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.like(Group::getGroupAccount, keyword)
                .or()
                .like(Group::getGroupName, keyword);
        return list(lambdaQueryWrapper);
    }

    /**
     * 条件获取全部的群
     * @param getGroupDTO
     * @return
     */
    @Override
    public ListForm<GetGroupVO> getAllGroups(GetGroupDTO getGroupDTO) {
        QueryWrapper<Group> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(getGroupDTO.getGroupAccount())) {
            queryWrapper.eq("group_account", getGroupDTO.getGroupAccount());
        }
        if (StringUtils.isNotBlank(getGroupDTO.getGroupName())) {
            queryWrapper.eq("group_name", getGroupDTO.getGroupName());
        }
        if (getGroupDTO.getGroupLeaderId() != null) {
            queryWrapper.eq("group_leader_id", getGroupDTO.getGroupLeaderId());
        }
        // 添加状态查询条件
        if(getGroupDTO.getState() != null){
            queryWrapper.eq("state", getGroupDTO.getState());
        }
        Page<Group> groupPage = new Page<>(getGroupDTO.getPage(), getGroupDTO.getSize());
        IPage<Group> groupIPage = page(groupPage, queryWrapper);
        List<Group> groupList = groupIPage.getRecords();
        List<GetGroupVO> groupVOList = groupList.stream().map(group -> {
            GetGroupVO getGroupVO = new GetGroupVO();
            BeanUtil.copyProperties(group, getGroupVO);
            return getGroupVO;
        }).collect(Collectors.toList());
        return new ListForm<>(getGroupDTO.getPage(), getGroupDTO.getSize(),
                groupIPage.getPages(),
                groupIPage.getTotal(),
                groupVOList);
    }

    /**
     * 禁用群
     * @param groupId
     * @return
     */
    @Override
    @Transactional
    public R DisableGroup(String groupId) {
        //先查询这个群是否存在
        Group group = getById(groupId);
        if (group == null)
            return R.failed("id为" + groupId + "的群不存在");
        // 创建一个 UpdateWrapper 对象
        UpdateWrapper<Group> updateWrapper = new UpdateWrapper<>();
        // 设置 id 等于 userID 的条件
        updateWrapper.eq("id", groupId);
        // 设置 state 属性为 false
        updateWrapper.set("state", false);
        // 执行更新操作
        boolean result = update(updateWrapper);
        if (result) {
            //TODO: 通知
            //获取这个群的所有成员id
            List<Long> groupMemberIds = messageChannelMappingService.getGroupMemberIds(group.getId());
            //通知信息
            Map<String, Object> noticeData = new HashMap<>();
            GroupVO groupVO = DtoVoUtil.entityToDtoOrVo(group, GroupVO.class);
            noticeData.put("groupVO", groupVO);//被解散的群信息
            //给每一个成员发送通知
            groupMemberIds.forEach(id -> {
                //构建通知体
                NoticeVO noticeVO = NoticeVO.builder().noticeType(NoticeType.groupDisable)
                        .publishId(group.getId())
                        .consumerId(id)
                        .date(new Date())
                        .data(noticeData).build();
                //发送通知
                redisHashUtil.publishNotice(noticeVO);
            });
            return R.succeed("群聊已成功禁用");
        } else {
            return R.failed("禁用群聊失败");
        }
    }

    /**
     * 启用群
     * @param groupId
     * @return
     */
    @Override
    public R EnableGroup(String groupId) {
        //先查询这个群是否存在
        Group group = getById(groupId);
        if (group == null)
            return R.failed("id为" + groupId + "的群不存在");
        // 创建一个 UpdateWrapper 对象
        UpdateWrapper<Group> updateWrapper = new UpdateWrapper<>();
        // 设置 id 等于 userID 的条件
        updateWrapper.eq("id", groupId);
        // 设置 state 属性为 false
        updateWrapper.set("state", true);
        // 执行更新操作
        boolean result = update(updateWrapper);
        if (result) {
            //TODO: 通知
            //获取这个群的所有成员id
            List<Long> groupMemberIds = messageChannelMappingService.getGroupMemberIds(group.getId());
            //通知信息
            Map<String, Object> noticeData = new HashMap<>();
            GroupVO groupVO = DtoVoUtil.entityToDtoOrVo(group, GroupVO.class);
            noticeData.put("groupVO", groupVO);//被解散的群信息
            //给每一个成员发送通知
            groupMemberIds.forEach(id -> {
                //构建通知体
                NoticeVO noticeVO = NoticeVO.builder().noticeType(NoticeType.groupEnable)
                        .publishId(group.getId())
                        .consumerId(id)
                        .date(new Date())
                        .data(noticeData).build();
                //发送通知
                redisHashUtil.publishNotice(noticeVO);
            });
            return R.succeed("群聊已成功启用");
        } else {
            return R.failed("启用群聊失败");
        }
    }

    @Override
    public R<Group> getGroupByAccount(String account) {
        QueryWrapper<Group> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("group_account", account);
        Group group = baseMapper.selectOne(queryWrapper);
        if (group != null) {
            return R.succeed(group);
        }
        return R.failed("找不到" + account + "群");
    }
}
